package com.majun.config.standardfilter;

import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * 包装类对，提供对响应数据的处理和操作。
 * 输出页面响应不了 必须要有copyBodyToResponse
 * @author majun
 * @date 2024/12/02
 */
public class ResponseWrapper extends HttpServletResponseWrapper {
    /**
     * This class implements an output stream in which the data is written into a byte array.
     * The buffer automatically grows as data is written to it. The data can be retrieved using toByteArray() and toString().
     Closing a ByteArrayOutputStream has no effect. The methods in this class can be called after the stream has been closed without generating an IOException.
     */
    //输出到byte array
    private ByteArrayOutputStream buffer = null;
    private ServletOutputStream servletOutputStream = null;
    private PrintWriter writer = null;

    /**
     * 构造函数，传入原始的 HttpServletResponse 对象
     * @param resp
     * @throws IOException
     */
    public ResponseWrapper(HttpServletResponse resp) throws IOException {
        super(resp);
        buffer = new ByteArrayOutputStream();// 真正存储数据的流
        servletOutputStream = new OutputStreamWapper(buffer);
//        writer = new PrintWriter(new OutputStreamWriter(buffer, this.getCharacterEncoding()));
        writer = new PrintWriter(new OutputStreamWriter(buffer, StandardCharsets.UTF_8.name()));
    }

    /**
     * 重写 getOutputStream 方法，返回经过包装后的 ServletOutputStream 对象
     * 重载父类获取outputstream的方法
     *
     * @return {@link ServletOutputStream }
     * @throws IOException
     */
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return servletOutputStream;
    }

    /**
     * 重写 getWriter 方法，返回经过包装后的 PrintWriter 对象
     * 重载父类获取writer的方法
     *
     * @return {@link PrintWriter }
     * @throws UnsupportedEncodingException
     */
    @Override
    public PrintWriter getWriter() throws UnsupportedEncodingException {
        return writer;
    }

    /** 重载父类获取flushBuffer的方法 */
    @Override
    public void flushBuffer() throws IOException {
        if (servletOutputStream != null) {
            servletOutputStream.flush();
        }
        if (writer != null) {
            writer.flush();
        }
    }

    @Override
    public void reset() {
        buffer.reset();
    }

    /**
     * 获取响应数据，并指定字符集
     * 将out、writer中的数据强制输出到WapperedResponse的buffer里面，否则取不到数据
     *
     * @return {@link byte[] }
     * @throws IOException
     */
    public byte[] getResponseBody() throws IOException {
        flushBuffer();
        return buffer.toByteArray();
    }

    /**
     * 非常重要
     * 将out、writer中的数据强制输出到WapperedResponse的buffer里面，否则取不到数据
     */
    public void copyBodyToResponse() throws IOException {
        HttpServletResponse rawResponse = (HttpServletResponse) getResponse();
        this.buffer.writeTo(rawResponse.getOutputStream());
        this.buffer.reset();
        super.flushBuffer();
    }

    /**
     * 私有内部类，用于包装 ServletOutputStream 对象
     * 内部类，对ServletOutputStream进行包装
     *
     * @author majun
     * @date 2024/12/04
     */
    private class OutputStreamWapper extends ServletOutputStream {
        private ByteArrayOutputStream byteArrayOutputStream = null;

        /**
         * 构造函数，传入待包装的 ByteArrayOutputStream 对象
         * @param byteArrayOutputStream
         * @throws IOException
         */
        public OutputStreamWapper(ByteArrayOutputStream byteArrayOutputStream) throws IOException {
            this.byteArrayOutputStream = byteArrayOutputStream;
        }

        /**
         * 重写 write 方法，将指定字节写入输出流
         * @param b
         * @throws IOException
         */
        @Override
        public void write(int b) throws IOException {
            byteArrayOutputStream.write(b);
        }

        @Override
        public void write(byte[] b) throws IOException {
            byteArrayOutputStream.write(b, 0, b.length);
        }

        /**
         * 重写 isReady 方法，指示输出流是否准备好接收写入操作
         * @return boolean 始终返回 false，表示输出流未准备好接收写入操作
         */
        @Override
        public boolean isReady() {
            return false;
        }

        /**
         * 重写 setWriteListener 方法，设置写入监听器
         * @param listener
         */
        @Override
        public void setWriteListener(WriteListener listener) {

        }
    }

}